#ifndef PROPERTIES_H
#define PROPERTIES_H
#include<assert.h>
#include<string>
#include<map>
#include<exception>
#include<tbb/tbb_exception.h>

namespace property_types
{
enum info
{
	NIL,
	NUMBER,
	STRING,
	BOOLEAN,
};
} //namespace property_types

//exception: requested _type does not match actual property _type (no implicit casting)
class CPropertyTypeMismatch: public tbb::movable_exception<void*>
{
public:
	CPropertyTypeMismatch()
		:tbb::movable_exception<void*>(NULL)
	{}
	virtual const char* what() const throw()
	{
		return "requested property _type is incorrect";
	}
};

//var_property: variable property that can hold_ref simple Lua types (nil,number,boolean,string)
class	CVarProperty
{
private:
	union pod_data{
		void*				nil;
		double				number;
		bool				boolean;
	};

	pod_data				m_data;
	std::string				m_string;
	property_types::info	m_type;

public:
	CVarProperty()
		: m_type(property_types::NIL)
	{
		m_data.nil = NULL;
	}

	CVarProperty(const double val)
		: m_type(property_types::NUMBER)
	{
		m_data.number = val;
	}

	CVarProperty(const int val)
		: m_type(property_types::NUMBER)
	{
		m_data.number = (double)(val);
	}

	CVarProperty(const long val)
		: m_type(property_types::NUMBER)
	{
		m_data.number = (double)(val);
	}

	CVarProperty(const std::string& val)
		: m_type(property_types::STRING)
	{
		m_string = val;
	}

	CVarProperty(const char* val)
		: m_type(property_types::STRING)
	{
		m_string = val;
	}

	CVarProperty(const bool val)
		: m_type(property_types::BOOLEAN)
	{
		m_data.boolean = val;
	}

	CVarProperty(const CVarProperty& other)
		: m_type(other.m_type)
		, m_string(other.m_string)
		, m_data(other.m_data)
	{}

	CVarProperty& operator=(const CVarProperty& other)
	{
		m_string = other.m_string;
		m_data = other.m_data;
		m_type = other.m_type;
		return *this;
	}

	~CVarProperty()
	{
		//noop
	}

	property_types::info	get_type()
	{
		return m_type;
	}

	double	get_number() const
	{
		if(m_type!=property_types::NUMBER)
		{
			throw CPropertyTypeMismatch();
		}
		return m_data.number;
	}

	const std::string& get_string() const
	{
		if(m_type!=property_types::STRING)
		{
			throw CPropertyTypeMismatch();
		}
		return m_string;
	}

	bool get_boolean() const
	{
		if(m_type!=property_types::BOOLEAN)
		{
			throw CPropertyTypeMismatch();
		}
		return m_data.boolean;
	}

};

std::ostream& operator<<(std::ostream& stream, CVarProperty& self)
{
	switch(self.get_type())
	{
	case property_types::NUMBER:	{stream << self.get_number(); break;}
	case property_types::STRING:	{stream << '"' << self.get_string() << '"'; break;}
	case property_types::BOOLEAN:	{stream << self.get_boolean()?"true":"false"; break;}
	case property_types::NIL:		{stream << "nil"; break;}
	default:						{assert(0); break;}
	}
	return stream;
}

typedef std::map<std::string,CVarProperty>	TPropertyMap;

#endif  // endif of PROPERTIES_H
